package org.govhack.okcapital.services;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.govhack.okcapital.model.Dataset;
import org.govhack.okcapital.model.DatasetProfile;
import org.govhack.okcapital.model.DatasetRating;
import org.govhack.okcapital.model.DatasetWithData;
import org.springframework.stereotype.Service;

import au.com.bytecode.opencsv.CSVReader;

@Service
public class CsvDataService {
    
    final static int ID_INDEX = 0;
    final static int MAZLOW_INDEX = 1;
    final static int MULTIPLIER_INDEX = 2;
    final static int NAME_INDEX = 3;
    final static int DESC_INDEX = 4;
    
    final static int ADELAIDE_INDEX = 5;
    final static int BRISBANE_INDEX = 6;
    final static int CANBERRA_INDEX = 7;
    final static int DARWIN_INDEX = 8;
    final static int HOBART_INDEX = 9;
    final static int MELBOURNE_INDEX = 10;
    final static int PERTH_INDEX = 11;
    final static int SYDNEY_INDEX = 12;
    
    public enum City {
        Adelaide,
        Brisbane,
        Canberra,
        Darwin,
        Hobart,
        Melbourne,
        Perth,
        Sydney
    }
    
    Map<String, DatasetWithData> datasets;
    
    public CsvDataService() throws IOException {
        init();
    }
    
    /**
     * Hack due to time - could be grabbed
     * @param city
     * @return
     */
    private int cityToIndex(City city) {
        return city.ordinal() + ADELAIDE_INDEX;
    }
    
    private void init() throws IOException {
        datasets = new HashMap<String, DatasetWithData>();
        
        InputStream csvInputStream = this.getClass().getResourceAsStream("compiled.csv");
        CSVReader reader = new CSVReader(new InputStreamReader(csvInputStream));
        reader.readNext(); //skip header
        
        String [] nextLine;
        while ((nextLine = reader.readNext()) != null) {
            Dataset ds = new Dataset(nextLine[ID_INDEX], nextLine[NAME_INDEX], nextLine[DESC_INDEX], Integer.parseInt(nextLine[MAZLOW_INDEX]));
            double[] data = new double[8];
            double multiplier = Double.parseDouble(nextLine[MULTIPLIER_INDEX]);
            for (int i = 0; i < data.length; i++) {
                data[i] = Double.parseDouble(nextLine[ADELAIDE_INDEX + i]);
            }
            
            datasets.put(ds.getDatasetId(), new DatasetWithData(ds, data, multiplier));
        }
        
        reader.close();
    }
    
    public List<Dataset> getDatasets() {
        List<Dataset> result = new ArrayList<Dataset>();
        for (String dsId : datasets.keySet()) {
            result.add(datasets.get(dsId).dataset);
        }
        
        return result;
    }
    
    public List<DatasetRating> rateCity(City city, Map<String, DatasetProfile> profiles, boolean applyWeighting) {
        int cityIndex = city.ordinal();
        List<DatasetRating> ratings = new ArrayList<DatasetRating>();
        
        
        for (String dsId : datasets.keySet()) {
            DatasetWithData dwd = datasets.get(dsId);
            
            double max = dwd.getMax();
            double min = dwd.getMin();
            double value = dwd.data[cityIndex];
            
            double userWeight = 1;
            DatasetProfile profile = profiles.get(dsId);
            if (profile != null) {
                switch (profile.getWeighting()) {
                case 1:
                    userWeight = 0.25;
                    break;
                case 2:
                    userWeight = 0.5;
                    break;
                case 3:
                    userWeight = 1.0;
                    break;
                case 4:
                    userWeight = 2;
                    break;
                case 5:
                    userWeight = 4;
                    break;
                }
            }
            
            double mazlowWeight = 1.0;
            switch(dwd.dataset.getMazlowRank()) {
            case 1:
                mazlowWeight = 0.4;
                break;
            case 2:
                mazlowWeight = 0.3;
                break;
            case 3:
                mazlowWeight = 0.15;
                break;
            case 4:
                mazlowWeight = 1;
                break;
            case 5:
                mazlowWeight = 0.05;
                break;
            }
            
            double denom = (max - min);
            if (denom == 0) {
                denom = 1;
            }
            double mult = applyWeighting ? dwd.multiplier : 1.0; 
            double rating = mult * userWeight * ((value - min) / denom) * mazlowWeight;
            
            ratings.add(new DatasetRating(dsId, rating));
        }
        
        return ratings;
    }
    
    
}
